﻿#pragma once;

#include <mutex>
#include <string>
#include <boost/dll/shared_library.hpp>
#include <boost/dll/import.hpp>
#include <map>

#include "log.h"
#include <memory>
#include "utility.hpp"
#include "base_plug_module.hpp"


import fast_mutex;

 namespace fast{
    class Library {
    public:

        /**
         * @brief 加载 dll 库
         * @param path 路径
         * @return 模块类
         */
        bool GetModule(const std::string &path) {

            // load lib
            boost::dll::shared_library lib;
            try {
                lib.load(path);
            }
            catch (std::exception &e) {
                LOG_ERROR << "dll file path not found [ " << path << " ] ";
                LOG_STACK;
                return false;
            }

            // load fail
            if (!lib.is_loaded()) {
                LOG_ERROR << "dll load fail, path [ " << path << " ] ";
                return false;
            }

            // found create function
            if (!lib.has("create_plugin")) {
                LOG_ERROR << "not found CreateModule, path [ " << path << " ] ";
                return false;
            }

            // get function, function return shared  or original
#ifdef  SHARED_LIBRARY
            typedef std::shared_ptr<BasePlugModule> (plugin_api_create_t)();

#else
            typedef bool (plugin_api_create_t)(BasePlugModule *BasePlugModule);
#endif



            try {
                // make shared
                std::function<plugin_api_create_t> creator;
                std::shared_ptr<BasePlugModule> create_plugin_;
#ifdef  SHARED_LIBRARY
                creator = boost::dll::import_alias<plugin_api_create_t>(lib, shared_function_name);
                create_plugin_ = std::shared_ptr<BasePlugModule>(creator());
#else
                creator = boost::dll::import_alias<plugin_api_create_t>(lib,common_function_name);
            BasePlugModule * plugModule = nullptr;
            creator(plugModule);
            create_plugin_ = std::shared_ptr<BasePlugModule>(plugModule);
#endif

                if (fast_is_null(create_plugin_)) {
                    LOG_ERROR << "invoke create_plugin error, path [ " << path << " ] ";
                }

                {
                    fast::MutexGuard once_mutex(m_dll_mutex_);
                    // cache lib
                    auto old_dll = m_dll_map_.find(path);
                    if (old_dll != m_dll_map_.end()) {
                        old_dll->second.unload();
                    }
                    m_dll_map_[path] = lib;
                }

                // insert m_source_map_
                LOG_DEBUG << create_plugin_->GetName();

                {
                    fast::MutexGuard once_mutex(m_module_mutex_);
                    m_source_map_[create_plugin_->GetName()] = create_plugin_;
                }

            }
            catch (std::exception &e) {
                LOG_STACK;
                LOG_ERROR << e.what();
                return false;
            }
            return true;

        }

        /**
         * @brief 获取模块
         * @param key 模块的version name
         * @return 动态模块的弱指针 可能为空
         */
        BasePlugModule::weak_ptr GetSource(const std::string &key) {
            // using mutex resources
            std::weak_ptr<BasePlugModule> plug;

            {
                fast::MutexGuard once_mutex(m_module_mutex_);
                if (m_source_map_.count(key) != 0) {
                    plug = m_source_map_.at(key);
                }
            }

            return plug;
        }

        /// @brief 销毁所有dll，程序结束时必须手动释放资源
        void DestroySharedDll() {
            {

                fast::MutexGuard once_mutex(m_module_mutex_);

                // destroy function
                for (auto &item: m_source_map_) {
                    if (fast_not_null(item.second)) {
                        item.second.reset();
                    }
                }

                // destroy shared dll
                for (auto &item: m_dll_map_) {
                    item.second.unload();
                }
            }
        }

    private:
        /// @brief 模块
        std::map<std::string, BasePlugModule::ptr> m_source_map_;

        /// @brief 第三方模块锁
        std::mutex m_module_mutex_;

        /// @brief dll加载锁
        std::mutex m_dll_mutex_;

        /// @brief dll 内存池
        std::map<std::string, boost::dll::shared_library> m_dll_map_;

        /// @deprecated 共享指针方法名称
        std::string m_shared_function_name_;

        /// @deprecated 普通方法名称
        std::string m_common_function_name_;
    };
}